package com.dynamic.mybatis.core.parser.field;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.dynamic.mybatis.core.enums.CommandType;
import com.dynamic.mybatis.core.enums.ReturnType;
import com.dynamic.mybatis.core.enums.Type;
import com.dynamic.mybatis.core.enums.TypeAlias;
import com.dynamic.mybatis.core.metadata.DynamicMappedStatement;
import com.dynamic.mybatis.core.metadata.DynamicPage;
import com.dynamic.mybatis.core.metadata.MappedStatementMetaField;
import com.dynamic.mybatis.core.session.DynamicSqlSession;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Description
 * @Author xs
 * @Date 2022/11/17 11:05
 */
public abstract class DynamicMappedStatementOutputParameterFieldParser extends DynamicMappedStatementDataBaseFieldParser {

    private TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

    public DynamicMappedStatementOutputParameterFieldParser(DynamicSqlSession dynamicSqlSession) {
        super(dynamicSqlSession);
    }

    public MappedStatementMetaField outputParameterFieldParser(DynamicMappedStatement dms) {
        MappedStatementMetaField field = new MappedStatementMetaField();

        Type type = Type.getTypeByJavaTypeAlias(dms.getReturnType().getType());
        if (type != null) {
            field.setType(type.getJavaTypeAlias());
        } else if (dms.getCommandType() != CommandType.StandardCommand.SELECT) {
            if (dms.getReturnType() != null && ReturnType.StandardReturnType.BOOLEAN == dms.getReturnType()) {
                field.setType(Type.StandardType.BOOLEAN.javaTypeAlias);
            } else {
                field.setType(Type.StandardType.INTEGER.javaTypeAlias);
            }
        }
        return outputParameterFieldWrapper(field, dms);
    }


    /**
     * 　* @Description: 获取动态SQL结果集信息
     * 　* @param configuration mybatis configuration
     * 　* @param connection 数据库链接
     *
     * @param dms 原生SQL或者mybatis动态SQL
     */
    public MappedStatementMetaField outputParameterFieldParser(List<MappedStatementMetaField> databaseFields, DynamicMappedStatement dms, MappedStatement tempMappedStatement) {

        MappedStatementMetaField msf1 = null;
        MappedStatementMetaField msf2 = null;
        //当前动态被更新的结果集
        if (tempMappedStatement != null && CollectionUtils.isNotEmpty(tempMappedStatement.getResultMaps())) {
            msf1 = getJavaResultMapping(tempMappedStatement.getConfiguration(), tempMappedStatement.getResultMaps().get(0).getType(), tempMappedStatement.getResultMaps().get(0).getResultMappings());
            mergeDataBseToJavaBatch(databaseFields, msf1.getChildren(), Boolean.FALSE);
        }
        //原生未被改动的结果集
        MappedStatement mappedStatement = getConfiguration().getMappedStatement(dms.getId());
        if (mappedStatement != null && CollectionUtils.isNotEmpty(mappedStatement.getResultMaps())) {
            msf2 = getJavaResultMapping(mappedStatement.getConfiguration(), mappedStatement.getResultMaps().get(0).getType(), mappedStatement.getResultMaps().get(0).getResultMappings());
            mergeDataBseToJavaBatch(databaseFields, msf2.getChildren(), Boolean.FALSE);
        }
        MappedStatementMetaField msf = mergeMappedStatementMetaField(msf1, msf2);
        return outputParameterFieldParser(databaseFields, msf.getJavaTypeClass(), msf, dms);
    }

    /**
     * 　* @Description: 获取动态SQL结果集信息
     * 　* @param configuration mybatis configuration
     * 　* @param connection 数据库链接
     *
     * @param dms 原生SQL或者mybatis动态SQL
     */
    public MappedStatementMetaField outputParameterFieldParser(List<MappedStatementMetaField> databaseFields, Class resultType, MappedStatementMetaField msf, DynamicMappedStatement dms) {
        if (CollectionUtils.isNotEmpty(databaseFields)) {
            databaseFields.stream().forEach(f -> {
                if (f.getValidator() != null) {
                    f.getValidator().setEnable(Boolean.FALSE);
                }
            });
        }
        //基础类型mybatis有对应的TypeHandler
        //@see org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createResultObject  hasTypeHandlerForResultObject

        boolean hasTypeHandler = typeHandlerRegistry.hasTypeHandler(resultType, JdbcType.forCode(databaseFields.get(0).getJdbcType().getVendorTypeNumber()));

        if (dms.getReturnType() == ReturnType.StandardReturnType.COLLECT) {//数组
            msf.setType(Type.StandardType.ARRAY.name().toLowerCase());
            msf.setGenericType(TypeAlias.getTypeAlias(resultType));
            //  msf.setName(mappedStatement.getId());
        } else if (dms.getReturnType() == ReturnType.StandardReturnType.PAGE) {//分页
            msf.setType(Type.StandardType.STRUCT.name().toLowerCase());

            List<MappedStatementMetaField> children = new ArrayList<>();
            MappedStatementMetaField pageNumberField = new MappedStatementMetaField();
            pageNumberField.setType(Type.StandardType.BIGINT.javaTypeAlias);
            pageNumberField.setName("当前页");
            pageNumberField.setDefaultValue(1);
            pageNumberField.setField(DynamicPage.PAGE_NUMBER);
            children.add(pageNumberField);

            MappedStatementMetaField pageSizeField = new MappedStatementMetaField();
            pageSizeField.setType(Type.StandardType.BIGINT.javaTypeAlias);
            pageSizeField.setName("每页数量");
            pageSizeField.setField(DynamicPage.PAGE_SIZE);
            pageSizeField.setDefaultValue(10);
            children.add(pageSizeField);

            MappedStatementMetaField totalField = new MappedStatementMetaField();
            totalField.setType(Type.StandardType.BIGINT.javaTypeAlias);
            totalField.setName("总数量");
            totalField.setField(DynamicPage.TOTAL);
            totalField.setDefaultValue(20);
            children.add(totalField);


            MappedStatementMetaField recordsField = new MappedStatementMetaField();
            recordsField.setType(Type.StandardType.ARRAY.javaTypeAlias);
            if (hasTypeHandler) {
                recordsField.setType(TypeAlias.getTypeAlias(resultType));
                recordsField.setJdbcType(databaseFields.get(0).getJdbcType());
                recordsField.setGenericType(TypeAlias.getTypeAlias(resultType));
            } else {
                recordsField.setGenericType(TypeAlias.getTypeAlias(resultType));
            }
            recordsField.setName("结果集");
            recordsField.setField(DynamicPage.RECORDS);
            if (!hasTypeHandler && CollectionUtils.isEmpty(msf.getChildren())) {
                recordsField.setChildren(mergeDataBseToJavaBatch(databaseFields, msf.getChildren(), Boolean.FALSE));
            }
            children.add(recordsField);

            msf.setChildren(children);

        } else if (dms.getReturnType() == ReturnType.StandardReturnType.MAP) {
            msf.setType(Type.StandardType.STRUCT.javaTypeAlias);
        } else if (hasTypeHandler) {//基本数据类型
            msf.setType(TypeAlias.getTypeAlias(resultType));
            msf.setJdbcType(databaseFields.get(0).getJdbcType());
        } else {
            msf.setGenericType(TypeAlias.getTypeAlias(resultType));
        }
        if (!hasTypeHandler && CollectionUtils.isEmpty(msf.getChildren())) {
            msf.setChildren(mergeDataBseToJavaBatch(databaseFields, msf.getChildren(), Boolean.FALSE));
        }
        return outputParameterFieldWrapper(msf, dms);
    }


    public MappedStatementMetaField outputParameterFieldWrapper(MappedStatementMetaField field, DynamicMappedStatement dms) {
        if (StringUtils.isBlank(dms.getResultWrapper())) {
            return field;
        }
        MappedStatementMetaField next = new MappedStatementMetaField();
        next.setType(Type.StandardType.STRUCT.javaTypeAlias);
        MappedStatementMetaField first = next;
        MappedStatementMetaField pre = null;
        String lastKey = null;
        String[] keys = dms.getResultWrapper().split("\\.");
        for (int i = 0; i < keys.length; i++) {
            String key = lastKey = keys[i];
            MappedStatementMetaField temp = new MappedStatementMetaField();
            temp.setType(Type.StandardType.STRUCT.javaTypeAlias);
            temp.setField(key);
            temp.setChildren(new ArrayList<>());
            next.setChildren(new ArrayList<>());
            next.getChildren().add(temp);
            pre = next;
            next = temp;
        }
        if (Objects.equals(field.getType(), Type.StandardType.STRUCT.javaTypeAlias)) {
            next.getChildren().addAll(field.getChildren());
        } else {
            pre.getChildren().clear();
            field.setField(lastKey);
            pre.getChildren().add(field);
        }
        return first;
    }

    private MappedStatementMetaField getJavaResultMapping(Configuration configuration, Class resultMapType, List<ResultMapping> resultMappings) {
        MappedStatementMetaField fieldVo = new MappedStatementMetaField();
        fieldVo.setJavaTypeClass(resultMapType);

        if (CollectionUtils.isEmpty(resultMappings)) {
            return fieldVo;
        }

        List<MappedStatementMetaField> fieldMappings = new ArrayList<>();
        int sort = 1;
        for (ResultMapping mapping : resultMappings) {
            MappedStatementMetaField childFieldVo = new MappedStatementMetaField();
            String field = com.dynamic.mybatis.core.utils.StringUtils.getNotEmpty(mapping.getProperty(), mapping.getColumn());
            childFieldVo.setField(field);
            childFieldVo.setName(field);
            childFieldVo.setSort((byte) sort++);
            String nestedResultMapId = mapping.getNestedResultMapId();
            ResultMap childrenMap;
            if (StringUtils.isNotBlank(nestedResultMapId) &&
                    (childrenMap = configuration.getResultMap(mapping.getNestedResultMapId())) != null && CollectionUtils.isNotEmpty(mapping.getComposites())) {
                childFieldVo.setJavaTypeClass(childrenMap.getType());
                mapping.getComposites();
                MappedStatementMetaField children = getJavaResultMapping(configuration, childrenMap.getType(), childrenMap.getResultMappings());
                childFieldVo.setChildren(children.getChildren());
            }
            fieldMappings.add(childFieldVo);
        }
        fieldVo.setChildren(fieldMappings);
        return fieldVo;
    }

}